package com.test.demo.config;

import org.apache.ibatis.mapping.DatabaseIdProvider;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.shardingsphere.api.config.sharding.KeyGeneratorConfiguration;
import org.apache.shardingsphere.api.config.sharding.ShardingRuleConfiguration;
import org.apache.shardingsphere.api.config.sharding.TableRuleConfiguration;
import org.apache.shardingsphere.api.config.sharding.strategy.StandardShardingStrategyConfiguration;
import org.apache.shardingsphere.shardingjdbc.api.ShardingDataSourceFactory;
import org.mybatis.spring.boot.autoconfigure.ConfigurationCustomizer;
import org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration;
import org.mybatis.spring.boot.autoconfigure.MybatisProperties;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ResourceLoader;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import java.util.Properties;

/**
 * 分片数据源配置，返回ShardingDataSource
 * @author Gao
 */
@Configuration
@AutoConfigureAfter({ShardingDataSourceConfig.class})
public class DataSourceConfig extends MybatisAutoConfiguration {

    @Resource
    private Map<String, DataSource> allShardingDataSource;
    /**
     * 逻辑数据库名称
     */
    @Value("${sharding.logic.dbName}")
    private String logicDbName;
    /**
     * 默认的数据库名称
     */
    @Value("${sharding.db0.name}")
    private String defaultDbName;

    public DataSourceConfig(MybatisProperties properties, ObjectProvider<Interceptor[]> interceptorsProvider,
                            ResourceLoader resourceLoader, ObjectProvider<DatabaseIdProvider> databaseIdProvider,
                            ObjectProvider<List<ConfigurationCustomizer>> configurationCustomizersProvider) {
        super(properties, interceptorsProvider, resourceLoader, databaseIdProvider, configurationCustomizersProvider);
    }

    @Bean(name = "shardingDataSource")
    public DataSource getShardingDataSource() throws SQLException {
        ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration();
        shardingRuleConfig.setDefaultDataSourceName(defaultDbName);
        // 添加user_info表配置
        shardingRuleConfig.getTableRuleConfigs().add(getUserInfoTableRuleConfiguration());
        // 所有分库分表的表在这里维护，多个表则用逗号间隔放在同一个字符串中
        shardingRuleConfig.getBindingTableGroups().add("user_info");
        Properties props = new Properties();
        return ShardingDataSourceFactory.createDataSource(allShardingDataSource, shardingRuleConfig, props);
    }

    /**
     * 事务管理器
     * @param shardingDataSource
     * @return
     */
    @Bean
    public DataSourceTransactionManager transactitonManager(DataSource shardingDataSource) {
        return new DataSourceTransactionManager(shardingDataSource);
    }

    /**
     * 将数据源交给mybatis管理
     * @param dataSource
     * @return
     * @throws Exception
     */
    @Override
    @Bean(name = "sqlSessionFactory")
    public SqlSessionFactory sqlSessionFactory(@Qualifier("shardingDataSource") DataSource dataSource) throws Exception {
        return super.sqlSessionFactory(dataSource);
    }

    /**
     * user_info表分片规则设置
     * 设置表规则，每新增一个表都需要重新设置规则，并且在getShardingDataSource中注册
     */
    @Bean
    public TableRuleConfiguration getUserInfoTableRuleConfiguration() {
        TableRuleConfiguration tableRuleConfiguration = new TableRuleConfiguration("user_info",
                logicDbName.concat("${0..1}.user_info${0..2}"));
        tableRuleConfiguration.setKeyGeneratorConfig(getKeyGeneratorConfiguration());
        // 设置user_info表的分库策略
        tableRuleConfiguration.setDatabaseShardingStrategyConfig(new StandardShardingStrategyConfiguration(
                "user_id", new ShardingDatabasePreciseAlgorithm()));
        // 设置user_info分表策略
        tableRuleConfiguration.setTableShardingStrategyConfig(new StandardShardingStrategyConfiguration(
                "user_id", new ShardingTablePreciseAlgorithm()));
        return tableRuleConfiguration;
    }

    /**
     * reward_student_point_detail表分布式主键生成策略
     * @return
     */
    private KeyGeneratorConfiguration getKeyGeneratorConfiguration() {
        Properties properties = new Properties();
        /*// 为每台机器配置不同的workerId
        String workerId = this.getWorkerId();
        // 如果服务器中没有配置文件，直接退出
        if (Objects.isNull(workerId)){
            System.exit(0);
        }
        properties.setProperty("worker.id", workerId);*/
        return new KeyGeneratorConfiguration("SNOWFLAKE", "uuid", properties);
    }

}
